﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using HeapModel;
using HeapModel.ProgInfo;
using HeapModel.RegionPartition;

namespace MetricExtractor
{
	internal sealed class SingletonAndSingleRgnTypeExtractor
	{
		internal static void Extract(RegionGraph hg, Dictionary<string, Tuple<bool, int>> singleregionandsizetypes)
		{
			Dictionary<String, Tuple<bool, int>> rgnoccourcountandsize = new Dictionary<String, Tuple<bool, int>>();

			//mark all the singleton and global shared nodes
			HashSet<RegionNode> visit = new HashSet<RegionNode>();
			Queue<RegionNode> wlist = new Queue<RegionNode>();
			HashSet<RegionNode> globalcandid = new HashSet<RegionNode>();

			List<Tuple<string, bool, RegionNode>> rootsinfo = hg.GetRootInfo();
			foreach(Tuple<string, bool, RegionNode> rr in rootsinfo)
			{
				if(rr.Item2 && rr.Item3 != null)
				{
					if(!visit.Contains(rr.Item3))
					{
						visit.Add(rr.Item3);
						wlist.Enqueue(rr.Item3);
					}

					globalcandid.Add(rr.Item3);
				}
			}

			while(wlist.Count != 0)
			{
				RegionNode rgn = wlist.Dequeue();
				foreach(RegionEdgeInfo ohrs in hg.FinalSuccMap[rgn])
				{
					string nns = ohrs.Offset.PathLast.NameValue;
					if(nns.EndsWith("[]") || nns.EndsWith("[?]") || nns.EndsWith("key") || nns.EndsWith("val"))
					{
						if(!visit.Contains(ohrs.Target))
						{
							visit.Add(ohrs.Target);
							wlist.Enqueue(ohrs.Target);
						}

						globalcandid.Add(ohrs.Target);
					}
				}
			}

			foreach(RegionNode hr in hg.Regions)
			{
				if(hr.RegionTypes.Count == 1 && hr.RegionTypes.First().IsSpecialNullType)
					continue;

				bool possibleglobal = globalcandid.Contains(hr);
				foreach(TypeIdentifier ttid in hr.RegionTypes)
				{
					if(!rgnoccourcountandsize.ContainsKey(ttid.NameValue))
						rgnoccourcountandsize.Add(ttid.NameValue, new Tuple<bool, int>(possibleglobal, hr.ConcreteNodeList.Count));
					else
					{
						Tuple<bool, int> occ = rgnoccourcountandsize[ttid.NameValue];
						rgnoccourcountandsize[ttid.NameValue] = new Tuple<bool, int>(false, Math.Max(occ.Item2, hr.ConcreteNodeList.Count));
					}
				}
			}

			foreach(KeyValuePair<String, Tuple<bool, int>> kvp in rgnoccourcountandsize)
			{
				if(!singleregionandsizetypes.ContainsKey(kvp.Key))
					singleregionandsizetypes.Add(kvp.Key, new Tuple<bool, int>(kvp.Value.Item1, kvp.Value.Item2));
				else
				{
					Tuple<bool, int> scc = singleregionandsizetypes[kvp.Key];
					singleregionandsizetypes[kvp.Key] = new Tuple<bool, int>(scc.Item1 && kvp.Value.Item1, Math.Max(scc.Item2, kvp.Value.Item2));
				}
			}
		}
	}

	/// <summary>
	/// Extracts metrics about single regions.
	/// </summary>
	internal sealed class IndivRegionMetrics
	{
		public static string[] RegionInfoHeaders = { "TotalRegions", "Immutable", "Atomic", "Linear", "Cycle", "System" };

		private readonly RegionShapeInfo m_rgnshapeinfo;
		private readonly RegionInEdgeInfo m_rgninoutinfo;

		internal IndivRegionMetrics(Dictionary<string, Tuple<bool, int>> singleregiontype)
		{
			this.m_rgnshapeinfo = new RegionShapeInfo(singleregiontype);
			this.m_rgninoutinfo = new RegionInEdgeInfo();
		}

		internal void Extract(RegionGraph hg)
		{
			this.m_rgnshapeinfo.Extract(hg);
			this.m_rgninoutinfo.Extract(hg);
		}

		internal string CSVFormatRgn()
		{ return this.m_rgnshapeinfo.CSVFormat(); }

		internal string CSVFormatRgnInEdges()
		{ return this.m_rgninoutinfo.CSVFormat(); }
	}

	/// <summary>
	/// This class extracts a number of metrics based on the properties of individual regions, we extract information for them in
	/// in terms of both the types that have a property and count the number of regions that have a property.
	/// 
	/// TotalTypes: Number of types seen on heap.
	/// TotalRegions: Number of regions seen in all heaps.
	/// ImmutableTypes: Number of types seen that are immutable (String, Double...).
	/// ImmutableRegionCount: Number of regions seen that are immutable.
	/// SingletonType: This type always appears as a singleton.
	/// GlobalSharedType: This type always appears as an in a single region (probably globally shared in some way).
	/// AtomicShapeTypeCount: Number of types that always appear in regions with no internal connectivity.
	/// AtomicShapeRegionCount: Number of regions with no internal connectivity.
	/// LinearShapeTypeCount: Number of types that may appear in a region with orderable shape (List, Tree, Dag).
	/// LinearShapeRegionCount: Number of regions with orderable shape (List, Tree, Dag).
	/// CycleShapeTypeCount: Number of types that may appear in a region with cylic shape (Cycle).
	/// CycleRegionTypeCount: Number of regions with cylic shape (Cycle).
	/// SystemTypeCount: Number of types that are from the Java Systems libraries.
	/// SystemRegionCount: Number or regions consisiting only of Java Systems libraries.
	/// </summary>
	internal sealed class RegionShapeInfo
	{
		private int TotalRegions = 0;
		private int ImmutableRegionCount = 0;
		private int AtomicRegionCount = 0;
		private int LinearRegionCount = 0;
		private int CycleRegionCount = 0;
		private int SystemRegionCount = 0;

		internal RegionShapeInfo(Dictionary<String, Tuple<bool, int>> singleregiontype)
		{
		}

		internal void Extract(RegionGraph hg)
		{
			//compute region centric information
			foreach(RegionNode hr in hg.Regions)
			{
				if(hr.RegionTypes.Count == 1 && hr.RegionTypes.First().IsSpecialNullType)
					continue;

				this.TotalRegions++;

				if(ExtractionSupport.AllTypesImmutable(hr.RegionTypes))
					this.ImmutableRegionCount++;

				RegionShapeEnum tst = hr.ExtraFacts.Shape;
				if(tst == RegionShapeEnum.Atomic)
					this.AtomicRegionCount++;
				else if(tst == RegionShapeEnum.List || tst == RegionShapeEnum.Tree || tst == RegionShapeEnum.Dag)
					this.LinearRegionCount++;
				else
					this.CycleRegionCount++;
				
				if(hr.RegionTypes.All((TypeIdentifier tt) => tt.NameValue.StartsWith("System.")))
					this.SystemRegionCount++;
			}
		}

		/// <summary>
		/// Return all of the region centric info as a tuple of string, the first string is the info on a per type basis, the second based on counting regions
		/// </summary>
		internal string CSVFormat()
		{ return String.Format("{0}, {1}, {2}, {3}, {4}, {5}", this.TotalRegions, this.ImmutableRegionCount, this.AtomicRegionCount, this.LinearRegionCount, this.CycleRegionCount, this.SystemRegionCount); }
	}

	/// <summary>
	/// This class extracts a distribution of the ratios of various in-degree and ownership properties.
	/// MaxInRefs: For each type the max number of in edges to any region it appears in, {n | n.InEdges.count == k}.
	/// AlwaysOwned: For each type a boolean indicating if all regions it appears in are owned regions, {n | n.shape == singleton \land n.InEdges.Count == 1 \land in edge is NP}.
	/// SharedImmutable: For each type if it is shared (has mulitple in edges) and is immutable. 
	/// We track the same values on a per region basis as well
	/// </summary>
	internal sealed class RegionInEdgeInfo
	{
		private readonly int[] m_inedgebins;
		private int m_ownedcount;

		internal RegionInEdgeInfo()
		{
			this.m_inedgebins = new int[500]; //new int[6];
			this.m_ownedcount = 0;
		}

		internal void Extract(RegionGraph hg)
		{
			//region centric info compute
			foreach(RegionNode hr in hg.Regions)
			{
				bool isowned = ExtractionSupport.IsNodeAnOwnedRegion(hr, hg.FinalPredMap[hr]);
				if(isowned)
					this.m_ownedcount++;

				this.m_inedgebins[hg.FinalPredMap[hr].Count]++;
			}
		}

		internal string CSVFormat()
		{
			StringBuilder rsb = new StringBuilder();
			for(int i = 0; i < this.m_inedgebins.Length; ++i)
			{
				if(i != 0)
					rsb.Append(", ");
				rsb.Append(this.m_inedgebins[i].ToString());
			}

			return rsb.ToString();
		}
	}
}
